/*
 * lib2Desa - Another 2D game library in C++
 * Copyright (C) 2009 Erivaldo Xavier de Lima Filho
 *
 * This program is free software and is distributed under GNU General Public License version 3. You can modify and/or redistribute
 * it under the terms of GPL version 3 or posterior version. This program comes with NO WARRANTY, for more details, see LICENSE,
 * or <http://www.gnu.org/licenses>.
 *
 * Contact info:
 *
 *	Erivaldo Xavier de Lima Filho, desadoc@gmail.com
 *
 */

#ifndef _SHARED_PTR_HPP
#define _SHARED_PTR_HPP

#include "types.hpp"
#include "Core/debug.hpp"

#ifndef _NO_NAMESPACE
namespace lib2Desa {
#endif

struct ptr_ref {

	u32 owners;
	u32 observers;

	ptr_ref(void):
		owners(0), observers(0)
	{
	}
};

template <typename T>
class weak_ptr;

template <typename T>
class shared_ptr
{
	private:

		void init(ptr_ref* ref, T* p)
		{
			r = ref;
			ptr = p;

			if(r)
			{
				r->owners++;
				r->observers++;
			}
		}

		void deinit(void)
		{
			if(r)
			{
				r->owners--;
				r->observers--;

				if(!r->owners)
				{
					if(ptr)
					{
						delete ptr;
						ptr = null;
					}

					if(!r->observers)
						delete r;
				}
			}
		}

	public:

		ptr_ref* r;
		T* ptr;

		shared_ptr(T* p = null)
		{
			if(p)
			{
				r = new ptr_ref;
				r->owners = 1;
				r->observers = 1;
				ptr = p;
			} else {
				r = null;
				ptr = null;
			}
		}

		shared_ptr(const null_class& null):
			r(null), ptr(null)
		{
		}

		shared_ptr(const shared_ptr<T>& p)
		{
			init(p.r, p.ptr);
		}

		template <typename OT>
		shared_ptr(const shared_ptr<OT>& p)
		{
			init(p.r, dynamic_cast<T*>(p.ptr));
		}

		shared_ptr(const weak_ptr<T>& p);

		~shared_ptr(void)
		{
			deinit();
		}

		const shared_ptr<T>& operator=(null_class& null)
		{
			deinit();
			init(null, null);
			return *this;
		}

		const shared_ptr<T>& operator=(const shared_ptr<T>& p)
		{
			deinit();
			init(p.r, p.ptr);
			return *this;
		}

		T* get(void)
		{
			if(r&&r->owners)
				return ptr;
			else
				return ((T*) null);
		}

		const T* get(void) const
		{
			if(r&&r->owners)
				return ptr;
			else
				return ((T*) null);
		}

		T* operator->(void)
		{
			return get();
		}

		const T* operator->(void) const
		{
			return get();
		}

		bool operator==(const shared_ptr<T>& p) const
		{
			if(this == &p)
				return true;

			if(!r||!p.r)
				return false;

			return (get() == p.get());
		}

		template <typename OT>
		bool operator==(const shared_ptr<OT>& p) const
		{
			if(!r||!p.r)
				return false;

			return (((void*)get()) == ((void*)p.get()));
		}

		operator bool(void) const
		{
			return (get() != null);
		}

		operator const T*(void) const
		{
			return get();
		}

		operator T*(void)
		{
			return get();
		}

		T& operator*(void)
		{
			return (*get());
		}

		const T& operator*(void) const
		{
			return (*get());
		}

	friend class weak_ptr<T>;
};

template <typename T>
class shared_ptr<const T>
{
	private:

		void init(ptr_ref* ref, const T* p)
		{
			r = ref;
			ptr = p;

			if(r)
			{
				r->owners++;
				r->observers++;
			}
		}

		void deinit(void)
		{
			if(r)
			{
				r->owners--;
				r->observers--;

				if(!r->owners)
				{
					if(ptr)
					{
						delete ptr;
						ptr = null;
					}

					if(!r->observers)
						delete r;
				}
			}
		}

	public:

		ptr_ref* r;
		const T* ptr;

		shared_ptr(const T* p = null)
		{
			if(p)
			{
				r = new ptr_ref;
				r->owners = 1;
				r->observers = 1;
				ptr = p;
			} else {
				r = null;
				ptr = null;
			}
		}

		shared_ptr(const null_class& null):
			r(null), ptr(null)
		{
		}

		shared_ptr(const shared_ptr<T>& p)
		{
			init(p.r, p.ptr);
		}

		template <typename OT>
		shared_ptr(const shared_ptr<OT>& p)
		{
			init(p.r, dynamic_cast<const T*>(p.ptr));
		}

		shared_ptr(const weak_ptr<T>& p);

		~shared_ptr(void)
		{
			deinit();
		}

		const shared_ptr<const T>& operator=(null_class& null)
		{
			deinit();
			init(null, null);
			return *this;
		}

		const shared_ptr<const T>& operator=(const shared_ptr<T>& p)
		{
			deinit();
			init(p.r, p.ptr);
			return *this;
		}

		const T* get(void) const
		{
			if(r&&r->owners)
				return ptr;
			else
				return ((T*) null);
		}

		const T* operator->(void) const
		{
			return get();
		}

		bool operator==(const shared_ptr<T>& p) const
		{
			if(this == &p)
				return true;

			if(!r||!p.r)
				return false;

			return (get() == p.get());
		}

		template <typename OT>
		bool operator==(const shared_ptr<OT>& p) const
		{
			if(!r||!p.r)
				return false;

			return (((void*)get()) == ((void*)p.get()));
		}

		operator bool(void) const
		{
			return (get() != null);
		}

		operator const T*(void) const
		{
			return get();
		}

		const T& operator*(void) const
		{
			return (*get());
		}

	friend class weak_ptr<T>;
};

template <typename T>
class weak_ptr
{
	void init(ptr_ref* ref, T* p)
	{
		r = ref;
		ptr = p;

		if(r)
			r->observers++;
	}

	void deinit(void)
	{
		if(r)
		{
			r->observers--;

			if(!r->owners)
				if(!r->observers)
					delete r;
		}
	}

	public:

		ptr_ref* r;
		T* ptr;

		weak_ptr(void):
			r(null), ptr(null)
		{
		}

		weak_ptr(const shared_ptr<T>& p)
		{
			init(p.r, p.ptr);
		}

		weak_ptr(const weak_ptr<T>& p)
		{
			init(p.r, p.ptr);
		}

		template <typename OT>
		weak_ptr(weak_ptr<OT>& p)
		{
			init(p.r, dynamic_cast<T*>(p.ptr));
		}

		~weak_ptr(void)
		{
			deinit();
		}

		const weak_ptr<T>& operator=(const shared_ptr<T>& p)
		{
			deinit();
			init(p.r, p.ptr);
			return *this;
		}

		const weak_ptr<T>& operator=(const weak_ptr<T>& p)
		{
			deinit();
			init(p.r, p.ptr);
			return *this;
		}

		T* get(void)
		{
			if(r&&r->owners)
				return ptr;
			else
				return ((T*) null);
		}

		const T* get(void) const
		{
			if(r&&r->owners)
				return ptr;
			else
				return ((T*) null);
		}

		T* operator->(void)
		{
			return get();
		}

		const T* operator->(void) const
		{
			return get();
		}

		bool operator==(const weak_ptr<T>& p) const
		{
			if(this == &p)
				return true;

			if(!r||!p.r)
				return false;

			return (get() == p.get());
		}

		template <typename OT>
		bool operator==(const shared_ptr<OT>& p) const
		{
			if(!r||!p.r)
				return false;

			return (((void*)get()) == ((void*)p.get()));
		}

		operator bool(void) const
		{
			return (get() != null);
		}

		operator const T*(void) const
		{
			return get();
		}

		operator T*(void)
		{
			return get();
		}

		T& operator*(void)
		{
			return (*get());
		}

		const T& operator*(void) const
		{
			return (*get());
		}
};

template <typename T>
shared_ptr<T>::shared_ptr(const weak_ptr<T>& p)
{
	init(p.r, p.ptr);
}

template <typename T>
shared_ptr<const T>::shared_ptr(const weak_ptr<T>& p)
{
	init(p.r, p.ptr);
}

#ifndef _NO_NAMESPACE
}
#endif

#endif /*shared_ptr.hpp included*/
